﻿using Common;
using System.Diagnostics;
using System.Net.Sockets;
using System.Text;

//using static System.Runtime.InteropServices.JavaScript.JSType;

namespace TcpService
{
    public class EC3TcpClient
    {
        #region 配置参数

        private readonly string _ipAddress;
        private readonly int _port;

        // 同步对象
        private readonly SemaphoreSlim _commsLock = new SemaphoreSlim(1, 1); // 初始计数=1

        private TcpClient? _tcpClient;
        private NetworkStream? _stream;
        private const int _maxRetries = 10;

        public string DeviceName => $"驱动器(ip={_ipAddress})";

        #endregion 配置参数

        #region 构造函数与初始化

        public EC3TcpClient(string ip = "192.168.1.12", int port = 502)
        {
            _ipAddress = ip;
            _port = port;
        }

        #endregion 构造函数与初始化

        #region 连接管理

        private async Task EnsureConnectedAsync(CancellationToken ct = default(CancellationToken))
        {
            if (_tcpClient?.Connected ?? false) return;

            Disconnect();
            _tcpClient = new TcpClient();
            await _tcpClient.ConnectAsync(_ipAddress, _port, ct);
            _stream = _tcpClient.GetStream();
            _stream.ReadTimeout = 3000;  //ms
            _stream.WriteTimeout = 3000; //ms

            MyLog.Info($"{DeviceName}已连接");
        }

        public async Task ConnectAsync(CancellationToken ct = default(CancellationToken))
        {
            await EnsureConnectedAsync(ct);
        }

        public void Disconnect()
        {
            _stream?.Close();
            _tcpClient?.Close();
            _stream = null;
            _tcpClient = null;
        }

        #endregion 连接管理

        #region 核心通信方法

        private async Task<byte[]> Request(byte[] request, int expectedLength, CancellationToken ct = default(CancellationToken))
        {
            DebugSth.LogStep("tcp request 1");

            //await EnsureConnectedAsync();
            await _commsLock.WaitAsync(); // 异步等待锁
            DebugSth.LogStep("tcp request 2");
            try
            {
                MyLog.Info(deviceName: DeviceName, message: $"send[{BitConverter.ToString(request).Replace("-", " ")}]");
                DebugSth.LogStep("tcp request 3");

                // 为写入操作创建3秒超时
                using (var writeCts = CancellationTokenSource.CreateLinkedTokenSource(ct))
                {
                    DebugSth.LogStep("tcp request 4");
                    writeCts.CancelAfter(TimeSpan.FromSeconds(3));
                    DebugSth.LogStep("tcp request 5");

                    // 发送请求
                    try
                    {
                        await _stream!.WriteAsync(request, 0, request.Length, writeCts.Token);
                    }
                    catch (OperationCanceledException) when (ct.IsCancellationRequested)
                    {
                        throw;
                    }
                    catch (OperationCanceledException)
                    {
                        throw new Exception("通信超时");
                    }

                    DebugSth.LogStep("tcp request 6");
                }

                // 接收响应
                var response = new byte[expectedLength];
                int totalRead = 0;
                DebugSth.LogStep("tcp request 7");

                // 为读取操作创建3秒超时
                using (var readCts = CancellationTokenSource.CreateLinkedTokenSource(ct))
                {
                    DebugSth.LogStep("tcp request 8");
                    readCts.CancelAfter(TimeSpan.FromSeconds(3));
                    Stopwatch sw = Stopwatch.StartNew();
                    DebugSth.LogStep("tcp request 9");
                    while (totalRead < expectedLength)
                    {
                        DebugSth.LogStep("tcp request 10");
                        if (sw.ElapsedMilliseconds > 3000)
                        {
                            throw new Exception("通信超时,请检查网络连接");
                        }
                        DebugSth.LogStep("tcp request 11");

                        try
                        {
                            var read = await _stream.ReadAsync(response, totalRead, expectedLength - totalRead, readCts.Token);

                            DebugSth.LogStep("tcp request 12");

                            MyLog.Info(deviceName: DeviceName, message: $"recv[{BitConverter.ToString(response).Replace("-", " ")}]");
                            if (read == 0) throw new IOException("Connection closed");
                            totalRead += read;
                        }
                        catch (OperationCanceledException) when (ct.IsCancellationRequested)
                        {
                            throw;
                        }
                        catch (OperationCanceledException)
                        {
                            throw new Exception("通信超时");
                        }
                    }
                }
                return response;
            }
            catch (Exception)
            {
                throw;
            }
            finally { _commsLock.Release(); }
        }

        #endregion 核心通信方法

        #region 读写参数

        public async Task SetStdDo(bool sts = true, CancellationToken ct = default(CancellationToken))
        {
            byte[] doSts = [0xFF, 0xFF];
            if (!sts)
            {
                doSts = "\0\0"u8.ToArray();
            }
            await Write(618, doSts, ct);
            await Write(619, doSts, ct);
        }

        public async Task SetExtDo(bool sts = true, CancellationToken ct = default(CancellationToken))
        {
            byte[] doSts = { 0xFF, 0xFF };
            if (!sts)
            {
                doSts = new byte[] { 0x00, 0x00 };
            }
            ///
            await Write(620, doSts, ct);
            await Write(621, doSts, ct);
            await Write(622, doSts, ct);
            await Write(623, doSts, ct);
        }

        public async Task ClearAlarm(CancellationToken ct = default(CancellationToken))
        {
            // 641 Flag写入1
            byte[] cmdClearAlarm = { 0x00, 0x01, };
            await Write(641, cmdClearAlarm, ct);

            // 等待641返回 0
            await CheckClearAlarmIsOk(ct);
        }

        /// <summary>
        /// 打开热插拔
        /// </summary>
        /// <param name="ct"></param>
        /// <returns></returns>
        public async Task OpenRCB(CancellationToken ct = default(CancellationToken))
        {
            // 642 Flag写入1
            byte[] cmdClearAlarm = { 0x00, 0x01, };
            await Write(642, cmdClearAlarm, ct);

            // 等待641返回 0
            await OpenRCBIsOk(ct);
        }

        public async Task<Int16> ReadAlarmCount(CancellationToken ct = default(CancellationToken))
        {
            var ret = ToInt16(await Read(631, 1, ct));
            return ret;
        }

        public async Task<Int16> ReadAlaramCode(Int16 errIndex, CancellationToken ct = default(CancellationToken))
        {
            byte[] cmdReadErr = { (byte)(errIndex >> 8), (byte)errIndex };
            await Write(632, cmdReadErr, ct);
            await Task.Delay(50);
            var errCode = ToInt16(await Read(633, 1, ct));
            return errCode;
        }

        public async Task<string> ReadSn(UInt16 axis = 1, UInt16 index = 0x5020, UInt16 subIndex = 0x0C, Int16 dataType = 4, UInt16 dataLen = 2, CancellationToken ct = default(CancellationToken))
        {
            byte[] totalBytes = { };
            for (int i = 0; i < 8; i++)
            {
                UInt16 sub = (UInt16)(subIndex + i);
                // 写入轴号，索引，子索引，数据类型，数据长度
                {
                    byte[] sendData =
                        {
                    (byte)(axis >> 8), (byte)axis,
                    (byte)(index >> 8), (byte)index,
                    (byte)(sub >> 8), (byte)sub,
                    (byte)(dataType >> 8), (byte)dataType,
                    (byte)(dataLen >> 8), (byte)dataLen,
                };
                    await Write(610, sendData, ct);
                }

                // 615 Flag写入2
                {
                    byte[] sendData = { 0x00, 0x02 };
                    await Write(615, sendData, ct);
                }
                // 等待615返回 0
                {
                    await CheckRWIsOk(ct);
                }

                // 读取616 数据 并解析
                {
                    byte[] recv = await Read(616, dataLen, ct);
                    byte[] tmp = { recv[2], recv[3], recv[0], recv[1] };

                    totalBytes = totalBytes.Concat(tmp).ToArray();
                }
            }

            return Encoding.ASCII.GetString(totalBytes);
        }

        public async Task<object> Read(UInt16 axis, UInt16 index, UInt16 subIndex, Int16 dataType, UInt16 dataLen, CancellationToken ct = default(CancellationToken))
        {
            // 写入轴号，索引，子索引，数据类型，数据长度
            {
                byte[] sendData =
                    {
                    (byte)(axis >> 8), (byte)axis,
                    (byte)(index >> 8), (byte)index,
                    (byte)(subIndex >> 8), (byte)subIndex,
                    (byte)(dataType >> 8), (byte)dataType,
                    (byte)(dataLen >> 8), (byte)dataLen,
                };
                await Write(610, sendData, ct);
            }

            // 615 Flag写入2
            {
                byte[] sendData = { 0x00, 0x02 };
                await Write(615, sendData, ct);
            }
            // 等待615返回 0
            {
                await CheckRWIsOk(ct);
            }

            // 读取616 数据 并解析
            {
                byte[] recv = await Read(616, dataLen, ct);

                switch (dataType)
                {
                    case 1: //U8
                        return ToByte(recv);

                    case -1://I8
                        return ToSbyte(recv);

                    case 2: //U16
                        return ToUInt16(recv);

                    case 4: //U32
                        return ToUInt32(recv);

                    case -2://I16
                        return ToInt16(recv);

                    case -4://I32
                        return ToInt32(recv);

                    case 5://float
                        return ToFloat(recv);

                    default:
                        throw new Exception($"{DeviceName}错误的数据类型{dataType}");
                }
            }
        }

        public async Task Write(UInt16 axis, UInt16 index, UInt16 subIndex, Int16 dataType, UInt16 dataLen, object data, CancellationToken ct = default(CancellationToken))
        {
            // 写入 轴号，索引，子索引，数据类型，数据长度
            {
                byte[] sendData =
                    {
                    (byte)(axis >> 8), (byte)axis,
                    (byte)(index >> 8), (byte)index,
                    (byte)(subIndex >> 8), (byte)subIndex,
                    (byte)(dataType >> 8), (byte)dataType,
                    (byte)(dataLen >> 8), (byte)dataLen,
                };
                await Write(610, sendData, ct);
            }
            // 616写入数据
            {
                byte[] sendData = ToBytes(data);

                if (sendData != null)
                {
                    await Write(616, sendData, ct);
                }
            }
            // 615 写入数据
            {
                byte[] sendData = { 0x00, 0x01 };
                await Write(615, sendData, ct);
            }
            // 等待615反馈写入成功
            {
                await CheckRWIsOk(ct);
            }
        }

        private async Task CheckRWIsOk(CancellationToken ct = default(CancellationToken))
        {
            Int16 errRet = -1;
            Int16 okRet = 0;
            for (int i = 0; i < 1000; i++)
            {
                var ret = ToInt16(await Read(615, 1, ct));

                if (errRet == ret)
                {
                    throw new Exception("615返回-1,发送标识寄存器指示读写数据失败");
                }
                else if (okRet == ret)
                {
                    break;
                }
                else
                {
                }
            }
        }

        private async Task OpenRCBIsOk(CancellationToken ct = default(CancellationToken))
        {
            Int16 okRet = 0;
            short ret=0;
            for (int i = 0; i < 100; i++)
            {
                 ret = ToInt16(await Read(642, 1, ct));

                if (okRet == ret)
                {
                    break;
                }
                else
                {
                }
                await Task.Delay(50,ct);
            }
            if (ret!=okRet)
            {
                throw new Exception($"热插拔打开失败");
            }

        }

        private async Task CheckClearAlarmIsOk(CancellationToken ct = default(CancellationToken))
        {
            Int16 errRet = -1;
            Int16 okRet = 0;
            for (int i = 0; i < 1000; i++)
            {
                var ret = ToInt16(await Read(641, 1, ct));

                if (errRet == ret)
                {
                    throw new Exception("641返回-1,清除报警失败");
                }
                else if (okRet == ret)
                {
                    break;
                }
                else
                {
                }
            }
        }

        // 写入多个保持寄存器
        private async Task Write(Int16 start, byte[] data, CancellationToken ct = default(CancellationToken))
        {
            // 帧头           len  设备地址    操作码 起始地址 寄存器数量  数据长度  数据
            // 00 01 00 00 00 09   01          10     00 00    00 01       02        00 ff
            // 00 01 00 00 00 0B 01 10 02 68 00 02 04 00 02 00 03
            // 00 01 00 00 00 09 01 10 02 68 00 01 02 00 06

            //[18:23:36.371]发→◇00 01 00 00 00 0B 01 10 02 68 00 02 04 00 02 00 03 □
            //[18:23:36.380]收←◆00 01 00 00 00 06 01 10 02 68 00 02
            //[18:23:46.841]发→◇00 01 00 00 00 09 01 10 02 68 00 01 02 00 06 □
            //[18:23:46.850]收←◆00 01 00 00 00 06 01 10 02 68 00 01

            int resisterCount = (byte)(data.Length / 2);
            int totalLen = 7 + resisterCount * 2;

            byte[] request = { 0x00, 0x01, 0x00, 0x00, 0x00, (byte)totalLen, 0x01, 0x10,
                (byte)(start >> 8), (byte)start, (byte)(resisterCount >> 8), (byte)resisterCount, (byte)(resisterCount*2), };
            request = request.Concat(data).ToArray();
            await Request(request, 12, ct);
        }

        private async Task<byte[]> Read(Int16 start, int len, CancellationToken ct = default(CancellationToken))
        {
            //[18:21:19.706]发→◇00 01 00 00 00 06 01 03 02 68 00 08
            //[18:21:19.715]收←◆00 01 00 00 00 13 01 03 10 00 06 00 03 00 00 00 00 00 00 00 00 00 00 00 00
            //[18:22:07.216]发→◇00 01 00 00 00 06 01 03 02 68 00 01
            //[18:22:07.224]收←◆00 01 00 00 00 05 01 03 02 00 06

            byte[] request = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x06,0x01,0x03,
                (byte)(start >> 8), (byte)start, (byte)(len >> 8), (byte)len, };

            var response = await Request(request, 9 + len * 2, ct);

            return response.Skip(9).ToArray();
        }

        #endregion 读写参数

        #region 数据转换

        private byte[] RestoreData(byte[] value)
        {
            byte[] bytes;
            if (value.Length == 2)
            {
                if (BitConverter.IsLittleEndian)
                {
                    bytes = value.Reverse().ToArray();
                }
                else
                {
                    bytes = value;
                }
            }
            else if (value.Length == 4)
            {
                var tmp = value;
                if (BitConverter.IsLittleEndian)
                {
                    tmp = value.Reverse().ToArray();
                }
                bytes = new byte[] { tmp[2], tmp[3], tmp[0], tmp[1], };
            }
            else if (value.Length == 1)
            {
                bytes = value;
            }
            else
            {
                throw new Exception($"{DeviceName}反转数据长度校验失败");
            }
            return bytes;
        }

        private byte[] ReserveData(byte[] value)
        {
            byte[] bytes;
            if (value.Length == 2)
            {
                if (BitConverter.IsLittleEndian)
                {
                    bytes = value.Reverse().ToArray();
                }
                else
                {
                    bytes = value;
                }
            }
            else if (value.Length == 4)
            {
                bytes = new byte[] { value[2], value[3], value[0], value[1], };
                if (BitConverter.IsLittleEndian)
                {
                    bytes = bytes.Reverse().ToArray();
                }
            }
            else if (value.Length == 1)
            {
                bytes = value;
            }
            else
            {
                throw new Exception($"{DeviceName}反转数据长度校验失败");
            }
            return bytes;
        }

        private sbyte ToSbyte(byte[] value)
        {
            if (value.Length != 2)
            {
                throw new Exception($"{DeviceName}解析数据长度异常");
            }
            return (sbyte)value[1];
        }

        private Int16 ToInt16(byte[] value)
        {
            if (value.Length != 2)
            {
                throw new Exception($"{DeviceName}解析数据长度异常");
            }
            return BitConverter.ToInt16(ReserveData(value), 0);
        }

        private Int32 ToInt32(byte[] value)
        {
            if (value.Length != 4)
            {
                throw new Exception($"{DeviceName}解析数据长度异常");
            }
            return BitConverter.ToInt32(ReserveData(value), 0);
        }

        private byte ToByte(byte[] value)
        {
            if (value.Length != 2)
            {
                throw new Exception($"{DeviceName}解析数据长度异常");
            }
            return value[1];
        }

        private UInt16 ToUInt16(byte[] value)
        {
            if (value.Length != 2)
            {
                throw new Exception($"{DeviceName}解析数据长度异常");
            }
            return BitConverter.ToUInt16(ReserveData(value), 0);
        }

        private UInt32 ToUInt32(byte[] value)
        {
            if (value.Length != 4)
            {
                throw new Exception($"{DeviceName}解析数据长度异常");
            }
            return BitConverter.ToUInt32(ReserveData(value), 0);
        }

        private float ToFloat(byte[] value)
        {
            if (value.Length != 4)
            {
                throw new Exception($"{DeviceName}解析数据长度异常");
            }
            return BitConverter.ToSingle(ReserveData(value), 0);
        }

        private byte[] ToBytes(object value)
        {
            if (value is sbyte i8)
            {
                return new byte[] { 0x00, (byte)i8 };
            }
            else if (value is Int16 i16)
            {
                var data = BitConverter.GetBytes(i16);

                return RestoreData(data);
            }
            else if (value is Int32 i32)
            {
                var data = BitConverter.GetBytes(i32);

                return RestoreData(data);
            }
            else if (value is byte u8)
            {
                var data = new byte[] { 0x00, u8 };

                return RestoreData(data);
            }
            else if (value is UInt16 u16)
            {
                var data = BitConverter.GetBytes(u16);

                return RestoreData(data);
            }
            else if (value is UInt32 u32)
            {
                var data = BitConverter.GetBytes(u32);

                return RestoreData(data);
            }
            else if (value is float f32)
            {
                var data = BitConverter.GetBytes(f32);

                return RestoreData(data);
            }
            else
            {
                throw new Exception($"{DeviceName}错误的数据类型{value.GetType().Name}");
            }
        }

        #endregion 数据转换

        #region 资源清理

        public void Dispose()
        {
            Disconnect();
            GC.SuppressFinalize(this);
        }

        #endregion 资源清理
    }
}